package rabid;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

public class Cluster {
	private String[] toBrowse;
	
	public String[] results;
	
	//public Set results;

	private HashMap hashMap;

	private String[] fileNames;

	private int numFiles;

	private String[] words;
	
	private File[] fileInfo;

	boolean[] filesUsed;

	Center[] centers;

	public Cluster(HashMap map, String[] strings, int count, boolean[] files) {
		hashMap = map;
		Set mapSet = map.keySet();
		Iterator it = mapSet.iterator();
		int i = 0;
		
		words = new String[map.size()];//(String[]) map.keySet().toArray();
		while(it.hasNext()) {
			words[i] = (String)it.next();
			i++;
		}

		fileNames = strings;
		numFiles = count;
		filesUsed = files;
		fileInfo = setFileInfo();
		pickCentroids();
		cluster();
		fillResults();
		printClusters();
	}

	private void printClusters() {
		for(int i=0; i<fileInfo.length; i++){
			System.out.println("File " + i + " is at " + fileInfo[i].getCenter());
		}
		
	}

	/**
	 * @return
	 */
	private File[] setFileInfo() {
		File[] toReturn = new File[numFiles];
		for(int i=0; i< numFiles; i++){
			toReturn[i] = new File(fileNames[i], filesUsed[i], -1, i);
		}
		return toReturn;
	}

	public void cluster() {
		boolean movement = true;
		int maxMove = 10;
		int moves = 0;
		
		while (movement) {
			if(moves >= maxMove) {
				break;
			}
			movement = moveFiles();
			findNewCenters();
			moves++;
		}
	}

	/**
	 * @return
	 */
	private void fillResults() {

		results = new String[7];
		for(int i=0; i<7; i++){
			String result = centers[i].getTopWord();
			if(result != null && !exists(result, results)){
				results[i] = result;
			} else {
				results[i] = "";
			}
		}	
	}

	private boolean exists(String result, String[] results2) {
		boolean toReturn = false;
		
		for(int i=0; i<results2.length; i++){
			if(results2[i] == null) {
				//empty and doesnt exist
				return false;
			} else if(result.compareTo(results2[i]) == 0){
				return true;
			}
		}
		return toReturn;
	}

	public void pickCentroids() {
		int[] firstSeven = getFristSeven();
		centers = new Center[7];
		for (int i = 0; i < 7; i++) {
			if (fileNames[firstSeven[i]] != null) {
				centers[i] = new Center(firstSeven[i],
						getTopWords(firstSeven[i]));
			}
		}
	}

	/**
	 * @return
	 */
	private int[] getFristSeven() {
		int[] toReturn = new int[7];

		int i = 0;
		for (int place = 0; place < filesUsed.length; place++) {
			if(i == 7) {
				return toReturn;
			}
			if (filesUsed[place]) {
				toReturn[i] = place;
				i++;
			}
		}

		return toReturn;
	}

	/**
	 * @return
	 */
	private String[] getTopWords(int file) {
		String[] topWords = new String[7];
		int largest = 0;
		String topWord = "";
		for (int i = 0; i < words.length; i++) {
			int[] counts = (int[]) (hashMap.get(words[i]));
			if (counts[file] > largest) {
				largest = counts[file];
				topWord = words[i];
			}
		}
		int ceiling = largest;
		topWords[0] = topWord;

		for (int x = 1; x < 7; x++) {
			largest = 0;
			topWord = "";
			for (int i = 1; i < words.length; i++) {
				int[] counts = (int[]) (hashMap.get(words[i]));
				int count = counts[file];
				if ((count > largest) && count < ceiling) {
					largest = count;
					topWord = words[i];
				}
			}
			ceiling = largest;
			topWords[x] = topWord;
		}
		return topWords;
	}

	public boolean moveFiles() {
		boolean filesMoved = false;
		double smallest;
		int startCenter = -1;
		int endCenter = 0;
		for (int x = 0; x < fileNames.length; x++) {
			if (filesUsed[x]) {
				startCenter = fileInfo[x].getCenter();
				smallest = Integer.MAX_VALUE;
				for (int i = 0; i < centers.length; i++) {
					if(centers[i].getName() != -1){
						double dist = distanceBetween(x, centers[i].getName());
						if (dist < smallest) {
							smallest = dist;
							endCenter = i;
						}
					}
				}
				fileInfo[x].setCenter(endCenter);
			}
			if (startCenter - endCenter != 0) {
				filesMoved = true;
			}
		}
		return filesMoved;
	}

	//TODO: find the file closest to the center of the points
	public void findNewCenters() {
		int[] sum = new int[words.length];
		for(int i=0; i<7; i++){
			int fileCount =0;
			for(int x=0; x<fileInfo.length; x++){
				if(fileInfo[x].getCenter() == i){
					fileCount++;
					for(int y=0; y<words.length; y++){
						int[] counts = (int[])hashMap.get(words[y]);
						sum[y] += counts[fileInfo[x].getPosition()];
					}
				}
			}
			
			if(fileCount == 0) {
				centers[i] = new Center(-1, new String[7]);
			} else {
				sum = findAverage(sum, fileCount);
				int closest = findClosestFile(sum);
				centers[i]=new Center(closest, getTopWords(closest));
			}
		}
		

	}

	/**
	 * @param sum
	 * @return
	 */
	private int findClosestFile(int[] sum) {
		int toReturn = 0;
		double smallestDistance = distanceToPoint(0, sum);
		for(int i=1; i<numFiles; i++){
			double toCompare = distanceToPoint(i, sum);
			if(toCompare < smallestDistance){
				smallestDistance = toCompare;
				toReturn = i;
			}			
		}
		return toReturn;
	}

	/**
	 * @param i
	 * @param sum
	 * @return
	 */
	private double distanceToPoint(int file, int[] sum) {
		double toReturn = 0;
		for (int i = 1; i < words.length; i++) {
			int[] counts = (int[]) (hashMap.get(words[i]));
			int count1 = counts[file];
			int count2 = sum[i];
			toReturn += Math.pow((count2 - count1), 2);
		}
		return Math.sqrt(toReturn);
	}

	/**
	 * @param sum
	 * @return
	 */
	private int[] findAverage(int[] sum, int files) {
		int[] toReturn = sum;
		for(int i=0; i<sum.length; i++){
			toReturn[i] = (sum[i]/files);
		}
		return toReturn;
	}

	public double distanceBetween(int file1, int file2) {
		double toReturn = 0;
		for (int i = 1; i < words.length; i++) {
			int[] counts = (int[]) (hashMap.get(words[i]));
			int count1 = counts[file1];
			int count2 = counts[file2];
			toReturn += Math.pow((count2 - count1), 2);
		}
		return Math.sqrt(toReturn);
	}

	private class Center {
		public int name;

		public String[] value;

		public Center(int fileName, String[] words) {
			name = fileName;
			value = words;
		}

		public int getName() {
			return name;
		}

		public String[] getValue() {
			return value;
		}
		
		public String getTopWord(){
			return value[0];
		}
	}
	
	private class File{

		public String name;
		public boolean used;
		public int center;
		public int position;
		
		public File(String toName, boolean inUse, int inCenter, int pos){
			name = toName;
			used = inUse;
			center = inCenter;
			position = pos;
		}
		
		/**
		 * @return Returns the center.
		 */
		public int getCenter() {
			return center;
		}
		/**
		 * @param center The center to set.
		 */
		public void setCenter(int center) {
			this.center = center;
		}
		/**
		 * @return Returns the name.
		 */
		public String getName() {
			return name;
		}
		/**
		 * @param name The name to set.
		 */
		public void setName(String name) {
			this.name = name;
		}
		/**
		 * @return Returns the used.
		 */
		public boolean isUsed() {
			return used;
		}
		/**
		 * @param used The used to set.
		 */
		public void setUsed(boolean used) {
			this.used = used;
		}
		/**
		 * @return Returns the position.
		 */
		public int getPosition() {
			return position;
		}
		/**
		 * @param position The position to set.
		 */
		public void setPosition(int position) {
			this.position = position;
		}
	}
}